/***********************************************************************************
 * 文件名： smcp.c
 * 作者： 刘言
 * 版本： 4.1
 * 说明：
 * 		Smart Mist Communication Protocol (SMCP) 4.1 通信协议（网络层）的实现。
 *      单缓存覆盖机制：发送方每次发送数据都会复写缓存，所以发送方必须确保接收方已经处理了上
 *  一包数据才能发送下一包数据。
 * 修改记录：
 * 	2021/2/1: 初版。 刘言。
 *  2021/4/12: 增加加密通信功能。 刘言
 *  2021/4/14: 增加主机功能，主从可任意切换。
 *  2021/11/02: RETURN数据包结果使用-1(0XFF)表示数据未发完。
 *  2024/3/12: 支持4.1。刘言。
***********************************************************************************/
#include "polsys.h"
#include "smcp.h"

// 预处理

// #ifdef _SMCP_LINK_USE_CRC16
// #define _SMCP_MAX_RX_DATA_LEN (_SMCP_LINK_RX_BUFF_SIZE - 2)
// #else
// #define _SMCP_MAX_RX_DATA_LEN _SMCP_LINK_RX_BUFF_SIZE
// #endif

// 属性

static idata smcp_callback_t mCb = NULL;
#ifdef SMCP_USE_ENCRYPT
static bit mEncrypt = false;    // 加密标记（收到的是加密数据包）
static pdata u16 mAuthCode;     // 认证码
static pdata u16 mRandPassCode; // 随机密码
static pdata u32 mBackCode;     // 回应码
#endif

#if (SMCP_ROLE == 2)
static pdata smcp_role_t mRole = SMCP_ROLE_SLAVE;
#endif

#if (SMCP_ROLE != 1) // 以下为主机专用属性
static pdata u8 mTargetDevice = 0;
static pdata u16 mMaxSlavePkgLen = _SMCP_MAX_TX_DATA_LEN;
#endif

// #if (SMCP_ROLE != 0) // 以下为从机专用的属性
// static pdata u16 mMaxHostPkgLen = _SMCP_MAX_TX_DATA_LEN;
// #endif

// 内部函数声明

// static void SendPkg(u16 lenght, u8 *dat);

// 对外接口函数

IN_LINE void Smcp_Init(smcp_callback_t callback_func)
{
    SmcpLink_Init();
}

IN_LINE void Smcp_SetCb(smcp_callback_t cb)
{
    mCb = cb;
}

#if (SMCP_ROLE == 2)
// 设置本机角色
// role: SMCP_ROLE_SLAVE-从机，SMCP_ROLE_HOST-主机
FORCE_IN_LINE void Smcp_SetRole(smcp_role_t role)
{
    mRole = role;
}
#endif

#if (SMCP_ROLE != 1) // 以下为主机专用函数
// 设置目标设备（从机类型、ID）
FORCE_IN_LINE void Smcp_SetTargetDevice(u8 device_type)
{
    mTargetDevice = device_type;
}

// 主机发送 HANDSHAKE
void Smcp_HandShake()
{
    u8 buff[16];
    ((smcp_data_t *)buff)->Cmd = SMCP_CMD_HANDSHAKE;
    *((u32 *)(buff + 8)) = _SMCP_HS_VERSION;
    buff[12] = _SMCP_HS_MODEL;
    *((u16 *)(buff + 14)) = _SMCP_MAX_RX_DATA_LEN;
    SendPkg(16, buff);
}

// 设置是否加密，如有必要，在发送命令前设置。
// 只有当本机处于主机角色时才有效，不要在等待从机回应时进行切换。
FORCE_IN_LINE void Smcp_SetEcrypt(bool new_state)
{
#if (SMCP_ROLE == 2) // 可切换
    if (mRole == SMCP_ROLE_HOST)
        mEncrypt = new_state;
#elif (SMCP_ROLE == 0) // 仅主机
    mEncrypt = new_state;
#endif
}

// 发送READ命令
void Smcp_Read(u32 addr, u16 length)
{
    u8 buff[16];

    ((smcp_data_t *)buff)->Cmd = SMCP_CMD_READ;
    Smcp_FormatReadParam((smcp_data_t *)buff)->AddrType = 0;
    Smcp_FormatReadParam((smcp_data_t *)buff)->ReadLength = length;
    Smcp_FormatReadParam((smcp_data_t *)buff)->Addr = addr;
    SendPkg(16, buff);
}

// 发送 WRITE 命令
// length： 数据字节数，不能超过 _SMCP_MAX_TX_DATA_LEN 或者 从机报告的数据包最大长度-16，否则无效。
// 返回值：WRITE命令是否已发送，当参数不合法时返回false。
// 通过获取从机的回应数据包来确定写入是否成功。
bool Smcp_Write(u32 addr, u8 *dat, u16 length)
{
    u8 buff[16 + _SMCP_MAX_TX_DATA_LEN];
    if (length > _SMCP_MAX_TX_DATA_LEN)
        return false;
    if (length > mMaxSlavePkgLen - 16)
        return false;
    ((smcp_data_t *)buff)->Cmd = SMCP_CMD_WRITE;
    Smcp_FormatWriteParam((smcp_data_t *)buff)->AddrType = 0;
    Smcp_FormatWriteParam((smcp_data_t *)buff)->WriteLength = length;
    Smcp_FormatWriteParam((smcp_data_t *)buff)->Addr = addr;
    memcpy(Smcp_FormatWriteParam((smcp_data_t *)buff)->WData, dat, length);
    SendPkg(16 + length, buff);
    return true;
}

// 升级从机固件，此命令发出后从机固件会被擦除，并运行OkBoot。
void Smcp_Update()
{
    u8 buff[8];
    ((smcp_data_t *)buff)->Cmd = SMCP_CMD_UPDATE;
    SendPkg(8, buff);
}
#endif

#if (SMCP_ROLE != 0) // 以下为从机专用函数

void Smcp_Return(smcp_rt_t e, const u8 *dat, u16 length)
{
    u16 pkg_len = length + 2;
    SmcpLink_SendByte(0x55);
    SmcpLink_SendByte(0xAA);
    SmcpLink_SendByte(pkg_len >> 8); // 协议规定高字节在前
    SmcpLink_SendByte(pkg_len);
    SmcpLink_SendByte(SMCP_CMD_RETURN);
    SmcpLink_SendByte(e);
    if(dat)
    {
        while(length)
        {
            SmcpLink_SendByte(*dat);
            dat++;
            length--;
        }
    }
}
#endif

// 链路层事件
void _Smcp_EvLinkEvent(smcp_event_t e)
{
    switch (e)
    {
    case SMCP_EV_RECEIVED_PKG:
    {
        const u8 *buff = SmcpLink_GetReceiveData();
#if (SMCP_ROLE == 2)
        if (mRole == SMCP_ROLE_HOST) // 目前是主机
#endif
#if (SMCP_ROLE != 1) // 不是仅从机，需要主机代码
        {
#ifdef SMCP_USE_ENCRYPT // 解密并获取回应码是否错误的信息
            bool BackErr = false;
            if (mEncrypt) // 是加密通信状态
            {
                _Smcp_DecryptData((u8 *)buff, len, mRandPassCode); // 进行解密操作
                if (mBackCode != buff->BackCode)
                    BackErr = true; // 回应码错误
            }
#endif
            if (buff->DeviceType == mTargetDevice) // 是目标设备回应的数据包
            {
#ifdef SMCP_USE_ENCRYPT
                if (BackErr)
                    mCb(SMCP_EV_BACK_ERROR); // 上报事件：回应码错误
                else
#endif
                {
                    if (buff->Cmd == SMCP_CMD_HANDSHAKE) // 是从机返回的 HANDSHAKE
                    {
                        u32 rand = *(u32 *)(buff + 8);
                        mAuthCode = _Smcp_GetAuthCode(rand);
                        mMaxSlavePkgLen = *(u16 *)(buff + 18);
                    }
                    mCb(SMCP_EV_RECEIVED_PKG); // 上报事件：收到一包数据（已解密）
                }
            }
            else // 有从机不守规矩主动占用总线
            {
                mCb(SMCP_EV_BUS_ERROR);
            }
        }
#endif
#if (SMCP_ROLE == 2)
        else // 目前是从机
#endif
#if (SMCP_ROLE != 0) // 不是仅主机，需要从机代码
        {
#ifdef SMCP_USE_ENCRYPT // 解密并获取认证码是否错误的信息
            bool AuthErr = false;
            if (buff->RandPassCode != 0) // 是加密的数据包
            {
                mEncrypt = true;                                           // 标记最后一次收到的是加密数据包，返回的数据包需要加密
                mRandPassCode = buff->RandPassCode;                        // 暂存密码
                _Smcp_DecryptData((u8 *)buff + 2, len - 2, mRandPassCode); // 进行解密操作
                mBackCode = _Smcp_GetBackCode((u8 *)buff, len);            // 计算并保存回应码
                if (mAuthCode != buff->AuthCode)                           // 认证码错误
                    AuthErr = true;
            }
            else
                mEncrypt = false;
#endif

#ifdef SMCP_USE_ENCRYPT
            if (AuthErr)
                mCb(SMCP_EV_AUTH_ERROR); // 上报事件：认证码错误
            else
#endif
            {
                if (buff[0] == SMCP_CMD_ANTI)
                {
                    u8 buff[12];
                    Smcp_Encrypt(((smcp_anti_t *)buff)->Rand, &buff[4]);
                    buff[0] = 0x17;
                    SmcpLink_SendPkg(12,buff);
                }
                else
                {
                    mCb(SMCP_EV_RECEIVED_PKG); // 上报事件：收到一包数据（已解密）
                }
            }
        }
#endif
        break;
    }
    default:
        mCb(e); // 直接向上传递事件
        break;
    }
}

// 以下是私有函数

// // 发送一包数据
// // 填充 设备类型 、 密钥，加密处理，然后发送，因此调用前无需提供这些内容，一般只填充命令和参数（8 以后）。
// static void SendPkg(u16 lenght, u8 *dat)
// {
//     // 填充DATA中的通用部分
// #if (SMCP_ROLE == 2)
//     if(mRole == SMCP_ROLE_HOST) // 本机是主机
// #endif
// #if (SMCP_ROLE != 1) // 不是仅从机，需要主机代码
//     {
//         ((smcp_data_t *)dat)->DeviceType = mTargetDevice;
// #ifdef SMCP_USE_ENCRYPT
//         if (mEncrypt) // 本包需要加密
//         {
//             ((smcp_data_t *)dat)->RandPassCode = _Smcp_GetRand(); // 取一个新的随机数做密码
//             ((smcp_data_t *)dat)->AuthCode = mAuthCode; // 填认证码
//             mBackCode = _Smcp_GetBackCode(dat, lenght);
//             _Smcp_EncryptData(dat+2, lenght-2, ((smcp_data_t *)dat)->RandPassCode);
//         }
//         else // 本包不需要加密
//         {
//             ((smcp_data_t *)dat)->RandPassCode = 0;
//         }
// #else
//         ((smcp_data_t *)dat)->RandPassCode = 0;
// #endif
//     }
// #endif
// #if (SMCP_ROLE == 2)
//     else            // 本机是从机
// #endif
// #if (SMCP_ROLE != 0) // 不是仅主机，需要从机代码
//     {
//         ((smcp_data_t *)dat)->DeviceType = SMCP_DEVICE_TYPE;
// #ifdef SMCP_USE_ENCRYPT
//         if (mEncrypt) // 本包需要加密
//         {
//             ((smcp_data_t *)dat)->BackCode = mBackCode;
//             _Smcp_EncryptData(dat, lenght, mRandPassCode);
//         }
//         else // 本包不需要加密
//         {
//             ((smcp_data_t *)dat)->BackCode = 0;
//         }
// #else
//         ((smcp_data_t *)dat)->BackCode = 0;
// #endif
//     }
// #endif
//     SmcpLink_SendPkg(lenght, dat);
// }




